from os.path import *
from os import walk, listdir
import re
import logging
import os
import time
import calendar
import operator
from otherparty.common import *
logger = logging.getLogger(__name__)
debug = logger.debug

try:
    from xml.etree import cElementTree as ET
except ImportError:
    try:
        from xml.etree import ElementTree as ET
    except ImportError:
        try:
            import cElementTree as ET
        except ImportError:
            from elementtree import ElementTree as ET
			
def svn_date_to_timestamp(svn_date):
    """
    Parse an SVN date as read from the XML output and return the corresponding
    timestamp.
    """
    # Strip microseconds and timezone (always UTC, hopefully)
    # XXX there are various ISO datetime parsing routines out there,
    # cf. http://seehuhn.de/comp/pdate
    date = svn_date.split('.', 2)[0]
    time_tuple = time.strptime(date, "%Y-%m-%dT%H:%M:%S")
    return calendar.timegm(time_tuple)

svn_log_args = ['log', '--xml', '-v']
svn_info_args = ['info', '--xml']
svn_checkout_args = ['checkout', '-q']
svn_status_args = ['status', '--xml', '-v', '--ignore-externals']

_identity_table = "".join(map(chr, range(256)))
_forbidden_xml_chars = "".join(
    set(map(chr, range(32))) - set('\x09\x0A\x0D')
)


def strip_forbidden_xml_chars(xml_string):
    """
    Given an XML string, strips forbidden characters as per the XML spec.
    (these are all control characters except 0x9, 0xA and 0xD).
    """
    return xml_string.translate(_identity_table, _forbidden_xml_chars)
	
def parse_svn_log_xml(xml_string):
    """
    Parse the XML output from an "svn log" command and extract useful information
    as a list of dicts (one per log changeset).
    """
    l = []
    xml_string = strip_forbidden_xml_chars(xml_string)
    tree = ET.fromstring(xml_string)
    for entry in tree.findall('logentry'):
        d = {}
        d['revision'] = int(entry.get('revision'))
        author = entry.find('author')
        # Some revisions don't have authors, most notably the first revision
        # in a repository.
        d['author'] = author is not None and author.text or None
        d['date'] = svn_date_to_timestamp(entry.find('date').text)
        d['message'] = entry.find('msg').text or ""
        paths = d['changed_paths'] = []
        for path in entry.findall('.//path'):
            copyfrom_rev = path.get('copyfrom-rev')
            if copyfrom_rev:
                copyfrom_rev = int(copyfrom_rev)
            paths.append({
                'path': path.text,
                'action': path.get('action'),
                'copyfrom_path': path.get('copyfrom-path'),
                'copyfrom_revision': copyfrom_rev,
            })
        l.append(d)
    return l

def clean(str):
    return str.replace(' ','').replace('\n','').replace('\r','').replace('\t','')

def getRoot():
    file = abspath('.')
    root = abspath('\\')
    while root != file and not exists(join(file,'.hg')):        
        file = dirname(file)
    if root == file:
        raise Exception, '"'+abspath('.')+'" do not belong to a HSVG repo.'
    return file

HSVG_TAG_PRE = 'svn.r'
MAP_DEC_ST = {'modified':'MOD','unversioned':'UNK','added':'NEW','missing':'DEL','normal':'2UP','deleted':'DEL'}
MAP_UNDEC_ST = dict([(MAP_DEC_ST[x], x) for x in MAP_DEC_ST])
try:
    IS_HSVG_REPO = False
    ROOT_DIR = getRoot()
    HSVG_DIR = join(getRoot(),'.hg','hsvg')
    HSVG_IGNORE = join(HSVG_DIR,'ignore.txt')
    HSVG_INCLUDE = join(HSVG_DIR,'include.txt')
    HSVG_IGNORE_HG = join(ROOT_DIR,'.hgignore')
    IS_HSVG_REPO = exists(join(ROOT_DIR,'.svn'))
except Exception:
    pass

def getSvnUrl(file='.'):
    """Obtiene url base del repo SVN"""
    return run_svn(['info',join(ROOT_DIR,abspath(file))]).replace('\r','').split('\n')[1][4:].replace('\n','').replace(' ','')

def readFile(filename):
    return '\n'.join(open(filename))

def getIncludeFolders():
    try:
        return map(lambda y: clean(y),filter(lambda x: len(x)>0,readFile(HSVG_INCLUDE).split('\n')))
    except Exception:
        return []

def statusSvn():
	out = run_svn(['st','--xml'] + getIncludeFolders())
	ret = {}
	for entry in ET.fromstring(out).findall('.//entry'):
		key = MAP_DEC_ST[entry.find('.//wc-status').get('item')]
		value = entry.get('path')
		if key in ret:
			ret[key].append(value)
		else:
			ret[key] = [value]
	logger.debug(ret)
	return ret

def getSvnUpFiles():
	"""Obtiene archivos a actualizar de svn."""
	xml = run_svn(['st','-u','--xml']+getIncludeFolders())
	tree = ET.fromstring(xml)
	return map(lambda x:x.get('path'), filter(lambda x: x.find('.//repos-status') != None,tree.findall('.//entry')))

def isHsvgTag(tag):
	return HSVG_TAG_PRE in tag
	
def maxi(x,y):
	if int(re.findall(r'\d+:',x)[0][:-1]) > int(re.findall(r'\d+:',y)[0][:-1]):
		return x
	else:
		return y
	
def getSvnLocalRev():
	"""Obtiene ultimo nro de revision de la working folder svn"""	
	out = run_hg(['tags']).split('\n')
	try:
		return int(re.findall('\d+',reduce(maxi, filter(isHsvgTag, out)))[0])
	except Exception, inst:
		print inst
		return 0	
	
def updateSvnRepo(doTag=True):
	"""Trae ultimos cambios del repo Svn."""
	files = getIncludeFolders()
	out = run_svn(['up']+files)
	if out != '':
		debug(out)
		lastRev = re.findall(r'\d+',out)[-1]
		if(doTag):
			print run_hg(['ci','-Am','commit '+lastRev])
			print run_hg(['tag','-l','-f','svn.r'+lastRev])